#include "gui.text.h"

// Constructor
TOB::UIText::UIText(uint16 x, uint16 y, uint16 w, uint16 h):
  UIWindow(x, y, w, h) {
  this->x = this->y = scrollX = scrollY = 0;
  password = false;
  multiline = false;
  String empty;
  lines.push_back(empty);
}


// Destructor
TOB::UIText::~UIText() {
}


// Set focus
void TOB::UIText::focus() {
  running = true;
  showCursor(true);
  setCursor(x, y);

  while(running) {
    getCursor(x, y);
    actX = x + scrollX;
    actY = y + scrollY;
    Char ch = getChar();
    
    switch(ch) {
     case 27:
      try {
        eventTab();
      } catch(std::bad_function_call) {
      }
      break;

     case '\n':
      if(!multiline) { 
        try{
          eventReturn();      
        } catch(std::bad_function_call) { }
        running = false; 
      } else { 
        split(); 
      } 
      break;

     case '\b':
     case KEY_BACKSPACE:
     case 127:
      remove();
      break;

     case KEY_DC:
      removeNext();
      break;

     case KEY_LEFT:
      moveX(-1); 
      break;

     case KEY_RIGHT:
      moveX(1); 
      break;

     case KEY_UP:
      if(multiline) { moveY(-1); } 
      break;

     case KEY_DOWN:
      if(multiline) {  moveY(1); }
      break;

     case '\t':
      try {
        eventTab();
      } catch(std::bad_function_call) {
        insert(' ');
        insert(' '); 
      }
      break;

     default:
      insert(ch);
      break;

    }
  }
}


// Insert single char
void TOB::UIText::insert(Char in) {
  String &line = lines[actY];
  line.insert(actX, 1, in);
  if(line.size() >= width && 
    (actX == line.size() || x == width-1)) {
    ++scrollX;
  } else { ++x; }
  printLine();
  setCursor(x, y);
}


// Remove single char
void TOB::UIText::remove() {
  String &line = lines[actY];
  
  if(scrollX) { --scrollX; } 
  else if(x) { --x; } 
  else { return join(); }

  line.erase(actX-1, 1);
  printLine();
  setCursor(x, y);
}


// Remove next single char
void TOB::UIText::removeNext() {
  String &line = lines[actY];
  
  if(actX == line.size()) { return; } 
  else if(scrollX) { --scrollX; }

  line.erase(actX, 1);
  printLine();
  setCursor(x, y);
}


// Split line
void TOB::UIText::split() {
  String &line = lines[actY];
  String nline = line.substr(actX);
  line = line.substr(0, actX);
  lines.insert(lines.begin() + actY + 1, nline);

  if(y < height - 1) { ++y; } 
  else { ++scrollY; }

  printBlock();
  setCursor(x = 0, y);
}


// Join line
void TOB::UIText::join() {
  if(!actY) { return; }

  String &line = (lines[actY-1] += lines[actY]);
  lines.erase(lines.begin() + actY);

  if(y) { 
    --y; 
    clear();
  } 
  else { --scrollY; }

  printBlock();
  x = (line.size() >= width) ? width - 1 : line.size();
  setCursor(x, y);
}


// Move x
void TOB::UIText::moveX(int8 direction) {
  String &line = lines[actY];
  if(direction > 0 && actX < line.size()) {
    if(x == width - 1) {
      ++scrollX;
      printLine();
    } else {
      ++x;
    }

  } else if(direction < 0) {
    if(x) {
      --x;
    } else if(actX) {
      --scrollX;
      printLine();
    }
  }
  setCursor(x, y);
}


// Move y
void TOB::UIText::moveY(int8 direction) {
  if(direction > 0 && actY < lines.size() - 1) {
    if(y == height - 1) {
      ++scrollY;
      printBlock();
    } else {
      ++y;
    }

  } else if(direction < 0) {
    if(y) {
      --y;
    } else if(actY) {
      --scrollY;
      printBlock();
    }
  }
  String &line = lines[scrollY + y];
  if(x >= line.size()) {
    x = line.size(); 
  } 
  setCursor(x, y);
}


// Print single line
void TOB::UIText::printLine() {
  setCursor(0, y);
  clearLine();
  String str = lines[actY].substr(scrollX, width);
  if(password) {
    str = String(str.size(), '*'); 
  }
  putString(str);
}


// Print entire block
void TOB::UIText::printBlock() {
  int _y = 0;
  scrollX = 0;
  for(uint32 i = scrollY; i < lines.size(); ++i) {
    setCursor(0, _y);
    clearLine();
    String str = lines[scrollY + _y].substr(0, width);
    putString(str);
    if(++_y== height) { break; }
  }
}


// Get text
String TOB::UIText::getText() {
  StringStream text;
  for(String &line: lines) {
    text << line << std::endl;
  }
  return text.str();
}
